use std::convert::TryFrom;

use actix_web::{get, post, Responder, web};
use http::HeaderMap;
use reqwest::Method;

use crate::api::ApiResult;
use crate::history::model::History;
use crate::middlewares::auth::AuthorizationService;
use crate::state::AppState;

use super::dao::IHistory;
use std::collections::HashMap;
use super::model::SqlID;

#[post("/add_item")]
async fn add_item(history: web::Json<History>, _auth: AuthorizationService, state: AppState) -> impl Responder {
  let mut history = history.into_inner();
  let history_clone = history.to_owned();
  let resp = request(history_clone).await;
  match resp {
    Ok(resp) => {
      info!("{}", resp);
      history.response = Some(resp.to_owned());
      match state.add_item(&history).await {
        Ok(res) => {
          info!("add_item {:?} res: {:?}", history, res);
          ApiResult::new().with_msg("ok").with_data(resp.to_owned())
        }
        Err(e) => {
          error!("add_item {:?} error: {:?}", history, e);
          ApiResult::new().code(400).with_msg(e.to_string())
        }
      }
    }
    Err(err) => {
      info!("{:?}", err);
      ApiResult::new().code(400).with_msg(err.to_string())
    }
  }
}


async fn request(history: History) -> Result<String, Box<dyn std::error::Error>> {
  let client = reqwest::Client::new();


  let params = history.params;
  let params = params.map_or(String::from(""), |x| { x });

  let mut header_map = HeaderMap::new();
  let headers = history.headers;

  let headers = headers.map(|x| {
    let x1 = x.to_owned();
    x1.split(";").map(|xx| xx.to_owned()).collect::<Vec<String>>()
  });


  // hack 搞不懂
  match headers {
    Some(h) => {
      for xx in h {
        let split: Vec<_> = xx.split(":").collect();
        if split.len() > 1 {
          let left = split.get(0).unwrap().to_owned().to_owned();
          let right = split.get(1).unwrap().to_string();
          header_map.insert(actix_web::http::HeaderName::try_from(&left).unwrap(), right.parse().unwrap());
        }
      }
    }
    None => {}
  }


  let body = match history.body {
    Some(data) => {
      Vec::from(data)
    }
    None => { Vec::from("") }
  };

  let result = serde_qs::from_str::<HashMap<String, String>>(&params);
  let request = client.request(Method::from_bytes(history.method.as_ref()).unwrap(), &history.url)
    .body(body)
    .headers(header_map)
    .query(&result.unwrap())
    .build();
  let resp = client.execute(request.unwrap()).await?.text().await?;
  Ok(resp)
}

// curl -v --data '{"name": "Bob", "email": "Bob@google.com", "password": "Bobpass"}' -H "Content-Type: application/json" -X POST localhost:8080/user/register
#[get("/list")]
async fn list(_auth: AuthorizationService,
              state: AppState) -> impl Responder {
  match state.list().await {
    Ok(res) => {
      info!("list res: {:?}", res);
      ApiResult::new().with_msg("ok").with_data(res)
    }
    Err(e) => {
      error!("list error: {:?}", e);
      ApiResult::new().code(400).with_msg(e.to_string())
    }
  }
}

#[get("/get_item/{id}")]
async fn get_item(id: web::Path<SqlID>, _auth: AuthorizationService, state: AppState) -> impl Responder {
  let id = id.into_inner();

  match state.get_item(id).await {
    Ok(res) => {
      info!("get_item {:?} res: {:?}", id, res);
      ApiResult::new().with_msg("ok").with_data(res)
    }
    Err(e) => {
      error!("get_item {:?} error: {:?}", id, e);
      ApiResult::new().code(400).with_msg(e.to_string())
    }
  }
}

#[post("/remove_item/{id}")]
async fn remove_item(id: web::Path<SqlID>, _auth: AuthorizationService, state: AppState) -> impl Responder {
  let id = id.into_inner();

  match state.remove_item(id).await {
    Ok(res) => {
      info!("remove_item {:?} res: {}", id, res);
      ApiResult::new().with_msg("ok").with_data(res)
    }
    Err(e) => {
      error!("remove_item {:?} error: {:?}", id, e);
      ApiResult::new().code(400).with_msg(e.to_string())
    }
  }
}


#[post("/clear")]
async fn clear(_auth: AuthorizationService, state: AppState) -> impl Responder {
  match state.clear().await {
    Ok(res) => {
      info!("clear res: {}", res);
      ApiResult::new().with_msg("ok").with_data(res)
    }
    Err(e) => {
      error!("clear  error: {:?}", e);
      ApiResult::new().code(400).with_msg(e.to_string())
    }
  }
}


pub fn init(cfg: &mut web::ServiceConfig) {
  cfg.service(add_item);
  cfg.service(get_item);
  cfg.service(remove_item);
  cfg.service(list);
  cfg.service(clear);
}
